home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / kernel / rpc / rpcDaemon.c < prev    next >
C/C++ Source or Header  |  1992-12-18  |  14KB  |  480 lines

  1. /*
  2.  * rpcDaemon.c --
  3.  *
  4.  *    The RPC daemon is in charge of reclaiming server processes,
  5.  *    and creating more if neccessary.  A server process is dedicated
  6.  *    to a particular client/channel for a series of RPCs.  After the
  7.  *    series (as determined by this daemon) the server is reclaimed
  8.  *    and made available to service RPC requests from other clients.
  9.  *
  10.  * Copyright (C) 1986 Regents of the University of California
  11.  * All rights reserved.
  12.  */
  13.  
  14. #ifndef lint
  15. static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/rpc/rpcDaemon.c,v 9.12 91/08/24 20:06:07 mottsmth Exp $ SPRITE (Berkeley)";
  16. #endif /* not lint */
  17.  
  18.  
  19. #include <sprite.h>
  20. #include <stdio.h>
  21. #include <rpc.h>
  22. #include <rpcInt.h>
  23. #include <rpcServer.h>
  24. #include <sync.h>
  25. #include <proc.h>
  26. #include <recov.h>
  27.  
  28. /*
  29.  * Server processes are dynamically created by the Rpc_Daemon process.
  30.  * Installation of new servers, their allocation, and control of the
  31.  * Rpc_Daemon process is all synchronized with the serverMutex master lock.
  32.  * rpcDaemon is the condition that is used to wakeup the Rpc_Deamon.
  33.  */
  34. static Sync_Semaphore serverMutex = Sync_SemInitStatic("Rpc:serverMutex");
  35. Sync_Condition rpcDaemon;
  36.  
  37. /*
  38.  * The timeout queue is used to periodically wake up the Rpc_Daemon.
  39.  */
  40. static Timer_QueueElement queueEntry;
  41. /*
  42.  * The state of Rpc_Daemon is used to know when to remove things
  43.  * from the timeout queue, and to lock out attempts at server allocation
  44.  * until the daemon is alive and can create server processes.
  45.  *    DAEMON_DEAD    The initial state of Rpc_Daemon.  This causes
  46.  *        RpcServerAlloc to discard any incomming messages.
  47.  *    DAEMON_TIMEOUT    The daemon has an entry in the timeout queue.
  48.  *    DAEMON_POKED    Set when rpcDaemon condition is notified to
  49.  *        let the daemon know if it woke up spuriously or not.
  50.  */
  51. #define DAEMON_TIMEOUT    1
  52. #define DAEMON_POKED    2
  53. #define DAEMON_DEAD    4
  54. static int daemonState = DAEMON_DEAD;
  55.  
  56. /*
  57.  * The server dispatcher signals its distress at not being able to dispatch
  58.  * a message because there are no server processes by incrementing this
  59.  * counter.  Rpc_Deamon notices this and creates more server processes.
  60.  * After max number of server processes have been created this is set
  61.  * to a negative number to prevent creating any more server processes.
  62.  */
  63. int         rpcNoServers = 0;
  64.  
  65.  
  66. /*
  67.  *----------------------------------------------------------------------
  68.  *
  69.  * Rpc_Daemon --
  70.  *
  71.  *    The main loop of the rpcDaemon process.  This process sleeps for
  72.  *    regular intervals and then pokes around looking for RPC server
  73.  *    processes that have been idle for a while.  Idle servers are
  74.  *    reclaimed by tidying up their connection with their old client
  75.  *    and making them available to handle RPC requests from other clients.
  76.  *    The other chore of the daemon is to create more RPC server processes
  77.  *    if the demand for them is high.  (Initially a few server processes
  78.  *    are created and the rest are created via this daemon.)
  79.  *
  80.  * Results:
  81.  *    This never returns.
  82.  *
  83.  * Side effects:
  84.  *    Evolves a server process's state from BUSY to AGING and back to FREE.
  85.  *    Creates new RPC server processes.
  86.  *
  87.  *----------------------------------------------------------------------
  88.  */
  89. void
  90. Rpc_Daemon()
  91. {
  92.     int pid;
  93.  
  94.     queueEntry.routine = RpcDaemonWakeup;
  95.     queueEntry.interval = 2 * timer_IntOneSecond;
  96.     queueEntry.clientData = (ClientData)NIL;
  97.  
  98.     Recov_CrashRegister(RpcCrashCallBack, (ClientData)NIL);
  99.  
  100.     while (TRUE) {
  101.     RpcDaemonWait(&queueEntry);
  102.     if (rpcNoServers > 0) {
  103.         /*
  104.          * The dispatcher has received requests that it couldn't handle
  105.          * because there were no available server processes.
  106.          */
  107.          if (Rpc_CreateServer(&pid) == SUCCESS) {
  108.          printf("RPC srvr %x\n", pid);
  109.          RpcResetNoServers(0);
  110.          } else {
  111.          printf("Warning: Rpc_Daemon: no more RPC servers\n");
  112.          RpcResetNoServers(-1);
  113.          queueEntry.interval = timer_IntOneSecond;
  114.          }
  115.     }
  116.     /*
  117.      * Reclaim servers from channels that have become idle.
  118.      * We are more aggressive about this if we have maxed
  119.      * out the number of server processes.
  120.      */
  121.     RpcReclaimServers(rpcNoServers < 0);
  122.     }
  123. }
  124.  
  125. /*
  126.  *----------------------------------------------------------------------
  127.  *
  128.  * Rpc_CreateServer(pidPtr) --
  129.  *
  130.  *      Create an RPC server process.  It won't be available to handle RPC
  131.  *      requests until it has run a bit and installed itself.  This
  132.  *      procedure is not monitored although it is called from the main program
  133.  *      and by the Rpc_Daemon process.  Currently the initialization done
  134.  *    in main is done before Rpc_Deamon begins running.
  135.  *
  136.  * Results:
  137.  *    A status from the process creating call.
  138.  *
  139.  * Side effects:
  140.  *    Create the server process, set up its state table entry, and
  141.  *    return the caller the process ID of the server.  The counter
  142.  *    rpcNumServers is incremented.
  143.  *
  144.  *----------------------------------------------------------------------
  145.  */
  146. ReturnStatus
  147. Rpc_CreateServer(pidPtr)
  148.     int *pidPtr;
  149. {
  150.     register ReturnStatus status;
  151.  
  152.     if (rpcNumServers >= rpcMaxServers) {
  153.     return(FAILURE);
  154.     }
  155.     /*
  156.      * Initialize the next slot in the server state table and create a
  157.      * process that goes with it.
  158.      */
  159.     rpcServerPtrPtr[rpcNumServers] = RpcInitServerState(rpcNumServers);
  160.     rpcNumServers++;
  161. #ifndef lint
  162.     /* Won't lint due to cast of function pointer to address. */
  163.     status = Proc_NewProc((Address)Rpc_Server, PROC_KERNEL, FALSE,
  164.         (Proc_PID *) pidPtr, "Rpc_Server", FALSE);
  165. #else    /* lint */
  166.     /*
  167.      * FOR LINTING ONLY!!!!  So that lint doesn't complain about status
  168.      * not being set, since I had to comment out the real setting due
  169.      * to function pointer cast.
  170.      */
  171.     status = SUCCESS;
  172. #endif /* lint */
  173.     if (status == SUCCESS) {
  174.     Proc_SetServerPriority(*((Proc_PID *) pidPtr));
  175.     }
  176.     return(status);
  177. }
  178.  
  179. /*
  180.  *----------------------------------------------------------------------
  181.  *
  182.  * RpcServerAlloc --
  183.  *
  184.  *    Match up an incoming message to its server.  The clientID field
  185.  *    of the header is used to identify the client.  This ID has to
  186.  *    already have been validated by RpcValidateClient.
  187.  *
  188.  * Results:
  189.  *    A pointer to the state of a server process.  The server is either
  190.  *    currently involved in a transaction with the client, or it was
  191.  *    the server for the client on the client's last transaction,
  192.  *    or it is newly allocated to the client.
  193.  *
  194.  * Side effects:
  195.  *    If this message is from a different client than the server's
  196.  *    previous client, the server's state is updated to identify
  197.  *    the new client.
  198.  *
  199.  *----------------------------------------------------------------------
  200.  */
  201. ENTRY RpcServerState *
  202. RpcServerAlloc(rpcHdrPtr)
  203.     RpcHdr *rpcHdrPtr;
  204. {
  205.     register int startIndex;
  206.     register int srvIndex;
  207.     register RpcServerState *srvPtr;
  208.     int    freeServer = -1;
  209.  
  210.     MASTER_LOCK(&serverMutex);
  211.     Sync_SemRegister(&serverMutex);
  212.  
  213.     if (daemonState == DAEMON_DEAD) {
  214.     /*
  215.      * The RPC system isn't up enough to accept requests.
  216.      */
  217.     srvPtr = (RpcServerState *)NIL;
  218.     goto unlock;
  219.     }
  220.     /*
  221.      * Start looking at the server indicated by the client's hint.
  222.      */
  223.     srvIndex = rpcHdrPtr->serverHint;
  224.     if (srvIndex < 0 || srvIndex >= rpcNumServers) {
  225.     srvIndex = 0;
  226.     }
  227.     startIndex = srvIndex;
  228.     do {
  229.     srvPtr = rpcServerPtrPtr[srvIndex];
  230.     if (srvPtr != (RpcServerState *)NIL) {
  231.         if (srvPtr->state & SRV_STUCK) {
  232.         /* skip this process */;
  233.         } else if (srvPtr->clientID == rpcHdrPtr->clientID &&
  234.         srvPtr->channel == rpcHdrPtr->channel) {
  235.         srvPtr->state &= ~SRV_FREE;
  236. #ifdef WOULD_LIKE
  237.         /* I would like this, but it's too much info. */
  238.         RpcAddServerTrace(srvPtr, NIL, FALSE, 12);
  239. #endif WOULD_LIKE
  240.         goto unlock;
  241.         } else if ((freeServer == -1) && (srvPtr->state & SRV_FREE)) {
  242.         freeServer = srvIndex;
  243.         }
  244.     }
  245.     srvIndex = (srvIndex + 1) % rpcNumServers;
  246.     } while (srvIndex != startIndex);
  247.  
  248.     if (freeServer != -1) {
  249.     /*
  250.      * Reassigning a free server to a new client.
  251.      */
  252.     srvPtr = rpcServerPtrPtr[freeServer];
  253.     srvPtr->state &= ~SRV_FREE;
  254.     RpcAddServerTrace(srvPtr, (RpcHdr *) NIL, FALSE, 13);
  255.     srvPtr->clientID = rpcHdrPtr->clientID;
  256.     srvPtr->channel = rpcHdrPtr->channel;
  257.     RpcAddServerTrace(srvPtr, (RpcHdr *) NIL, FALSE, 14);
  258.     } else {
  259.     /*
  260.      * No available server process yet.
  261.      */
  262.     srvPtr = (RpcServerState *)NIL;
  263.     RpcAddServerTrace((RpcServerState *)NIL, (RpcHdr *) rpcHdrPtr, TRUE,15);
  264.     if (rpcNoServers >= 0) {
  265. #ifdef BAD
  266.         /*
  267.          * This is where I used to have it and this is bad.  It doesn't
  268.          * catch requests after the number of server procs has maxed.
  269.          */
  270.         RpcAddServerTrace(NIL, rpcHdrPtr, TRUE, 15);
  271. #endif BAD
  272.         /*
  273.          * If rpcNoServers hasn't been set to -1 we can create more.
  274.          * Poke the Rpc_Daemon process so it can create one.
  275.          */
  276.         rpcNoServers++;
  277.         if (daemonState & DAEMON_TIMEOUT) {
  278.         (void)Timer_DescheduleRoutine(&queueEntry);
  279.         daemonState &= ~DAEMON_TIMEOUT;
  280.         }
  281.         daemonState |= DAEMON_POKED;
  282.         Sync_MasterBroadcast(&rpcDaemon);
  283.     }
  284.     }
  285. unlock:
  286.     MASTER_UNLOCK(&serverMutex);
  287.     return(srvPtr);
  288. }
  289.  
  290. /*
  291.  *----------------------------------------------------------------------
  292.  *
  293.  * RpcServerInstall --
  294.  *
  295.  *    Assign a server process to an entry in the server table.
  296.  *    This would be trivial if the server process could be
  297.  *    passed an argument, ie, a pointer to it's state table entry.
  298.  *
  299.  * Results:
  300.  *    A pointer to the state table entry for the server.
  301.  *
  302.  * Side effects:
  303.  *    Change the state of the server from NOTREADY to FREE.
  304.  *
  305.  *----------------------------------------------------------------------
  306.  */
  307. ENTRY RpcServerState *
  308. RpcServerInstall()
  309. {
  310.     RpcServerState *srvPtr;
  311.     register int i;
  312.     /*
  313.      * This synchronizes with RpcServerAlloc.  This will only be
  314.      * important if the server is getting requests as the machine boots.
  315.      */
  316.     MASTER_LOCK(&serverMutex);
  317.  
  318.     for (i=0 ; i<rpcNumServers ; i++) {
  319.     srvPtr = rpcServerPtrPtr[i];
  320.     if (srvPtr->state == SRV_NOTREADY) {
  321.         RpcAddServerTrace(srvPtr, (RpcHdr *) NIL, FALSE, 16);
  322.         srvPtr->state = SRV_FREE;
  323.         RpcAddServerTrace(srvPtr, (RpcHdr *) NIL, FALSE, 17);
  324.         goto unlock;
  325.     }
  326.     }
  327.     srvPtr = (RpcServerState *)NIL;
  328. unlock:
  329.     MASTER_UNLOCK(&serverMutex);
  330.     return(srvPtr);
  331. }
  332.  
  333. /*
  334.  *----------------------------------------------------------------------
  335.  *
  336.  * RpcCrashCallBack --
  337.  *
  338.  *    This is called when a remote client has crashed or rebooted.
  339.  *    If we have any RPC servers assigned to channels from that client
  340.  *    we have to mark them as unusable so we don't erroneously discard
  341.  *    client requests.  Otherwise the server allocation routine will
  342.  *    see new requests directed to a busy server process and discard them.
  343.  *    This is only needed if Rpc_Server processes can hang up on something,
  344.  *    which does happen from time to time.
  345.  *
  346.  * Results:
  347.  *    None.
  348.  *
  349.  * Side effects:
  350.  *    Marks any servers that are busy with this client as unusable
  351.  *    until their current RPC service procedure completes.
  352.  *
  353.  *----------------------------------------------------------------------
  354.  */
  355. /*ARGSUSED*/
  356. ENTRY void
  357. RpcCrashCallBack(clientID, data)
  358.     int clientID;        /* Host ID of crashed client */
  359.     ClientData data;        /* IGNORED */
  360. {
  361.     register RpcServerState *srvPtr;
  362.     register int i;
  363.  
  364.     MASTER_LOCK(&serverMutex);
  365.  
  366.     for (i=0 ; i<rpcNumServers ; i++) {
  367.     srvPtr = rpcServerPtrPtr[i];
  368.     if (srvPtr->state & SRV_BUSY) {
  369.         /*
  370.          * Mark the server as stuck if it is working for the dead client.
  371.          * It becomes unstuck upon completion of its current RPC.
  372.          */
  373.         if (srvPtr->clientID == clientID) {
  374.         srvPtr->state |= SRV_STUCK;
  375.         }
  376.     }
  377.     }
  378.     MASTER_UNLOCK(&serverMutex);
  379. }
  380.  
  381. /*
  382.  *----------------------------------------------------------------------
  383.  *
  384.  * RpcResetNoServers --
  385.  *
  386.  *    A tiny routine to synchronize access to the rpcNoServers counter.
  387.  *    This counter is used by the server dispatcher to communicate out
  388.  *    to the Rpc_Deamon that there are not enough server processes.
  389.  *    This is checked (unsynchronizedly...) by Rpc_Deamon and then
  390.  *    reset (if non-zero) via this routine.
  391.  *
  392.  * Results:
  393.  *    None.
  394.  *
  395.  * Side effects:
  396.  *    Reset the rpcNoServers counter to the indicated value.
  397.  *
  398.  *----------------------------------------------------------------------
  399.  */
  400. ENTRY void
  401. RpcResetNoServers(value)
  402.     int value;        /* New value for rpcNoServers */
  403. {
  404.     MASTER_LOCK(&serverMutex);
  405.     rpcNoServers = value;
  406.     MASTER_UNLOCK(&serverMutex);
  407. }
  408.  
  409. /*
  410.  *----------------------------------------------------------------------
  411.  *
  412.  * RpcDaemonWait --
  413.  *
  414.  *    Make the Rpc_Daemon process wait.  This has to be synchronized with
  415.  *    the routines that wakeup the daemon.  The serverMutex is used as
  416.  *    the lock for these routines.
  417.  *
  418.  * Results:
  419.  *    None.
  420.  *
  421.  * Side effects:
  422.  *    Puts the routine in the timeout queue under the protection of
  423.  *    the serverMutex master lock.
  424.  *
  425.  *----------------------------------------------------------------------
  426.  */
  427. ENTRY void
  428. RpcDaemonWait(queueEntryPtr)
  429.     Timer_QueueElement *queueEntryPtr;    /* Initialized timer queue item */
  430. {
  431.     MASTER_LOCK(&serverMutex);
  432.     if (daemonState & DAEMON_DEAD) {
  433.     daemonState &= ~DAEMON_DEAD;
  434.     }
  435.     if ((daemonState & DAEMON_TIMEOUT) == 0) {
  436.     daemonState |= DAEMON_TIMEOUT;
  437.     Timer_ScheduleRoutine(queueEntryPtr, TRUE);
  438.     }
  439.     while ((daemonState & DAEMON_POKED) == 0) {
  440.     Sync_MasterWait(&rpcDaemon, &serverMutex, FALSE);
  441.     if (sys_ShuttingDown) {
  442.         printf("Rpc_Daemon exiting.\n");
  443.         MASTER_UNLOCK(&serverMutex);
  444.         Proc_Exit(0);
  445.     }
  446.     }
  447.     daemonState &= ~DAEMON_POKED;
  448.     MASTER_UNLOCK(&serverMutex);
  449. }
  450.  
  451. /*
  452.  *----------------------------------------------------------------------
  453.  *
  454.  * RpcDaemonWakeup --
  455.  *
  456.  *    Called from the timeout queue to wakeup the Rpc_Daemon.  Grabs
  457.  *    the serverMutex lock and notifies rpcDaemon condition to
  458.  *    wakeup the Rpc_Daemon process.
  459.  *
  460.  * Results:
  461.  *    None.
  462.  *
  463.  * Side effects:
  464.  *    Notifies the rpcDaemon condition.
  465.  *
  466.  *----------------------------------------------------------------------
  467.  */
  468. /*ARGSUSED*/
  469. ENTRY void
  470. RpcDaemonWakeup(time, data)
  471.     Timer_Ticks time;        /* Time we timed out at. */
  472.     ClientData data;        /* NIL */
  473. {
  474.     MASTER_LOCK(&serverMutex);
  475.     daemonState &= ~DAEMON_TIMEOUT;
  476.     daemonState |= DAEMON_POKED;
  477.     Sync_MasterBroadcast(&rpcDaemon);
  478.     MASTER_UNLOCK(&serverMutex);
  479. }
  480.